package com.zhz.mall.common.util.transaction;

import com.zhz.mall.common.api.CommonResult;
import com.zhz.mall.common.api.ResultCode;
import com.zhz.mall.common.util.context.SpringApplicationContextHolder;
import com.zhz.mall.common.util.threadpool.ThreadPoolUtils;
import org.springframework.transaction.NoTransactionException;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.transaction.support.*;

import java.util.Objects;
import java.util.function.Consumer;

import static org.springframework.transaction.TransactionDefinition.PROPAGATION_REQUIRES_NEW;

/**
 * TransactionUtils
 * <p>
 * Spring 事务工具类
 *
 * @author zhouhengzhe
 */
public abstract class TransactionUtils {

    /**
     * 标记当前事务为回滚状态
     * <p>
     * 注：若当前上下文不存在事务, 则会抛出异常
     */
    public static void setRollbackOnly() throws NoTransactionException {
        // 注意：TransactionAspectSupport.currentTransactionStatus() 只对声明式事务（@Transactional）有用
        // 若是通过 TransactionTemplate 开启的事务, 虽然 isActualTransactionActive 也返回 true
        // 但是是无法通过 TransactionAspectSupport.currentTransactionStatus() 获取, 会抛出异常
        // 手动回滚 TransactionTemplate 开启的事务, 必须通过 TransactionCallback 中的 TransactionStatus 参数 setRollbackOnly

        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    }


    /**
     * 标记当前事务（若存在）为回滚状态
     * <p>
     * 注：只对声明式事务有效
     */
    public static void setRollbackOnlyIfExists() {
        if (isActualTransactionActive()) {
            try {
                setRollbackOnly();
            } catch (NoTransactionException ignored) {
            }
        }
    }


    /**
     * 判断当前上下文是否有事务
     */
    public static boolean isActualTransactionActive() {
        return TransactionSynchronizationManager.isActualTransactionActive();
    }


    /**
     * 在当前事务提交后执行
     */
    public static void afterCommit(Runnable task) {
        afterCommit(task, false);
    }


    /**
     * 在当前事务提交后执行, 如果不存在事务就立刻执行
     */
    public static void afterCommitIfExistsOrElseRightNow(Runnable task) {
        if (isActualTransactionActive()) {
            afterCommit(task);
        } else {
            task.run();
        }
    }

    /**
     * 在当前事务提交后执行, 如果不存在事务就立刻执行
     */
    public static void afterCommitIfExistsOrElseRightNowAsync(Runnable task) {
        if (isActualTransactionActive()) {
            afterCommit(task, true);
        } else {
            task.run();
        }
    }

    /**
     * 在当前事务提交后执行
     */
    public static void afterCommit(Runnable task, boolean async) {
        if (!isActualTransactionActive()) {
            throw new NoTransactionException("No transaction managed TransactionStatus in scope");
        }

        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
            @Override
            public void afterCommit() {
                super.afterCommit();
                if (async) {
                    ThreadPoolUtils.execute(task);
                } else {
                    task.run();
                }
            }
        });
    }


    /**
     * 在当前事务结束（提交/回滚）后执行
     *
     * @see TransactionSynchronization
     */
    public static void afterCompletion(Consumer<Integer> task) {
        afterCompletion(task, false);
    }

    /**
     * 如果当前存在事物，则在当前事务结束（提交/回滚）后执行
     * 否则，立刻执行
     * 当不存在事物是，执行方法，事物状态 status 为 {@link TransactionSynchronization#STATUS_COMMITTED}
     *
     * @see TransactionSynchronization
     */
    public static void afterCompletionIfExistsOrElseRightNow(Consumer<Integer> task) {
        if (isActualTransactionActive()) {
            afterCompletion(task, false);
        } else {
            task.accept(TransactionSynchronization.STATUS_COMMITTED);
        }
    }


    /**
     * 在当前事务结束（提交/回滚）后执行
     *
     * @see TransactionSynchronization
     */
    public static void afterCompletion(Consumer<Integer> task, boolean async) {
        if (!isActualTransactionActive()) {
            throw new NoTransactionException("No transaction managed TransactionStatus in scope");
        }

        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
            @Override
            public void afterCompletion(int status) {
                super.afterCompletion(status);
                if (async) {
                    ThreadPoolUtils.execute(() -> task.accept(status));
                } else {
                    task.accept(status);
                }
            }
        });
    }


    /**
     * 创建 TransactionTemplate
     */
    public static TransactionTemplate createTransactionTemplate() {
        PlatformTransactionManager platformTransactionManager = SpringApplicationContextHolder.getBean(PlatformTransactionManager.class);
        return new TransactionTemplate(platformTransactionManager);
    }


    /**
     * 创建 TransactionTemplate
     *
     * @param propagationBehavior 事务传播行为
     */
    public static TransactionTemplate createTransactionTemplate(int propagationBehavior) {
        TransactionTemplate transactionTemplate = createTransactionTemplate();
        transactionTemplate.setPropagationBehavior(propagationBehavior);
        return transactionTemplate;
    }


    /**
     * 开启新事物执行
     */
    public static void executeWithNewTransaction(Consumer<TransactionStatus> action) {
        createTransactionTemplate(PROPAGATION_REQUIRES_NEW).executeWithoutResult(action);
    }


    /**
     * 开启新事物执行
     */
    public static <T> T executeWithResultNewTransaction(TransactionCallback<T> action) {
        return createTransactionTemplate(PROPAGATION_REQUIRES_NEW).execute(action);
    }


    public static <T> T execute(TransactionCallback<T> action) {
        TransactionTemplate transactionTemplate = SpringApplicationContextHolder.getBean(TransactionTemplate.class);
        return transactionTemplate.execute(action);
    }


    public static void executeWithoutResult(Consumer<TransactionStatus> action) {
        TransactionTemplate transactionTemplate = SpringApplicationContextHolder.getBean(TransactionTemplate.class);
        transactionTemplate.executeWithoutResult(action);
    }


    /**
     * 当 result 失败时, 标记当前事务为回滚状态
     */
    public static <T> CommonResult<T> setRollbackOnly(CommonResult<T> result) throws NoTransactionException {
        if (Objects.isNull(result) || result.getCode() != ResultCode.SUCCESS.getCode()) {
            setRollbackOnly();
        }
        return result;
    }


    public static <T> CommonResult<T> setRollbackOnlyIfExists(CommonResult<T> result) {
        if (Objects.isNull(result) || result.getCode() != ResultCode.SUCCESS.getCode()) {
            setRollbackOnlyIfExists();
        }
        return result;
    }

}
